//manager.cc

#include "manager.h"
#include "cineplex.h"

Manager::Manager(char* name)
{
	myName = name;
	msgList = new MsgList();
	outgoing = new MsgList();
	msgLock = new Lock(myName);
	sleepCon = new Condition(myName);
	state = "Idle";
	clerks = new IterList();
	checkQueue = new IterList();
	currentGroup = new IterList();
	lineLengths = new IterList();
	msgsToSend = 0;
	repliesWaiting = 0;
	/*bools*/ 
	moviePlaying = 0; 
	checkLoop = 0;
	cashLoop = 0;
	
	tixCash = 0;
	foodCash = 0;
}

Manager::~Manager()
{

}

void
Manager::Start()
{
	numTixClerks = NUM_TIX_CLERKS;
	numFoodClerks = NUM_FOOD_CLERKS;
	numTixTakers = NUM_TIX_TAKERS;
	cinema = getCineplex();
	cinema->tixClerks->AppendMeTo(clerks);
	cinema->foodClerks->AppendMeTo(clerks);
	cinema->ticketTakers->AppendMeTo(clerks);
	
	if(!cinema->testPattern)
	{
		 printf("Starting manager %s...\n", myName);
		if(getCineplex()->runSchedulers) Scheduler();
	}else{
		if(cinema->testPattern == 4) CT4();
	}
}

void Manager::CT4()
{
	clerks->AppendMeTo(checkQueue);
	msgsToSend = numTixClerks;
	repliesWaiting = numTixClerks;
	((TicketClerk*)cinema->tixClerks->Get(1))->GoOnBreak();
	Scheduler();
	Scheduler();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	cinema->testAgentsLeft = cinema->testAgentsLeft - 1;
}

void
Manager::SendMessage(char* name, int argc, ...)
{
	msgLock->Acquire();
	
	//printf("%s rec msg \"%s\"\n",myName,name);
	
	IterList* args = new IterList();
	va_list vali;
	va_start(vali, argc);
	
	for ( int i = 0; i < argc; i++ )        
    {
        args->Append(va_arg ( vali, void* )); 
    }
	va_end(vali);
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
Manager::SendMessage(char* name, IterList* args)
{
	msgLock->Acquire();
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

//SCHEDULER
void
Manager::Scheduler()
{
	//printf("%s  -  %s\n", myName, state);
	bool runScheduler = getCineplex()->runSchedulers;
	do
	{
		//printf("%s running Scheduler...\n",myName);
		if(!strcmp(state,"Idle"))
		{
			IdleAction();
		}else if(!strcmp(state,"Available"))
		{
			AvailAction();
		}else if(!strcmp(state,"StartMovie"))
		{
			StartMovie();
			//state = "Idle";/////
		}else if(!strcmp(state,"DecideCashCheck"))
		{
			DecideCC();
		}else if(!strcmp(state,"BackToCheck"))
		{
			BackToCheck();
		}else if(!strcmp(state,"BackToCash"))
		{
			BackToCash();
		}else if(!strcmp(state,"CheckAskTixClerks"))
		{
			ChATC();
		}else if(!strcmp(state,"CheckWaitTixClerks"))
		{
			ChWTC();
		}else if(!strcmp(state,"CheckHandleTixClerks"))
		{
			ChHTC();
		}else if(!strcmp(state,"CheckSendTixClerks"))
		{
			ChSTC();
		}else if(!strcmp(state,"CheckAskFoodClerks"))
		{
			ChAFC();
		}else if(!strcmp(state,"CheckWaitFoodClerks"))
		{
			ChWFC();
		}else if(!strcmp(state,"CheckHandleFoodClerks"))
		{
			ChHFC();
		}else if(!strcmp(state,"CheckSendFoodClerks"))
		{
			ChSFC();
		}else if(!strcmp(state,"CheckAskTixTakers"))
		{
			ChATT();
		}else if(!strcmp(state,"CheckWaitTixTakers"))
		{
			ChWTT();
		}else if(!strcmp(state,"CheckHandleTixTakers"))
		{
			ChHTT();
		}else if(!strcmp(state,"CheckSendTixTakers"))
		{
			ChSTT();
		}else if(!strcmp(state,"CashAskTixClerks"))
		{
			CaATC();
		}else if(!strcmp(state,"CashWaitTixClerks"))
		{
			CaWTC();
		}else if(!strcmp(state,"CashAskFoodClerks"))
		{
			CaAFC();
		}else if(!strcmp(state,"CashWaitFoodClerks"))
		{
			CaWFC();
		}else if(!strcmp(state,"CashClearTixTakers"))
		{
			CaCTT();
		}else if(!strcmp(state,"Completed"))
		{
			runScheduler = 0;
		}
		currentThread->Yield();
	} while(runScheduler);
}

//ACTIONS
void Manager::IdleAction()
{
	// printf("Customers still running: %d\n",cinema->customersRunning);
	if(!cinema->customersRunning)
	{
		state = "Completed";
		return;
	}
	IterList* endMessage = msgList->Find("MovieOver");
	if(endMessage)
	{
		//printf("%s received Movie Over",myName);
		moviePlaying = 0;
	}
	
	if(cinema->testPattern == 4)
	{
		state = "CheckAskTixClerks";
		return;
	}
	
	if(!moviePlaying && cinema->theater->seatsFilled == 0)
	{
		if(getRandPercent() < 50)
		{
			state = "StartMovie";
		}else{
			state = "DecideCashCheck";
		}
	}else
	{
		state = "DecideCashCheck";
	}
}

void Manager::AvailAction()
{
	// printf("Customers still running: %d\n",cinema->customersRunning);
	IterList* endMessage = msgList->Find("MovieOver");
	if(endMessage)
	{
		//printf("%s received Movie Over",myName);
		moviePlaying = 0;
	}
	if(!moviePlaying && cinema->theater->seatsFilled == 0)
	{
		if(getRandPercent() < 50)
		{
			state = "StartMovie";
		}else{
			if(checkLoop) state = "BackToCheck";
			else if(cashLoop) state = "BackToCash";
		}
	}else
	{
		if(checkLoop) state = "BackToCheck";
		else if(cashLoop) state = "BackToCash";
	}
}

void Manager::StartMovie()
{
	cinema->tech->SendMessage("StartMovie", 0);
	cinema->lobby->SendMessage("NewMovieStarting",0);
	cinema->letPeopleIn = 1;
	moviePlaying = 1;
	printf("Manager is telling the MovieTechnnician to start the movie.\n");
	if(checkLoop || cashLoop) state = "Available";
	else state = "Idle";
}

void Manager::DecideCC()
{
	clerks->AppendMeTo(checkQueue);
	msgsToSend = numTixClerks;
	repliesWaiting = numTixClerks;
	currentGroup->Clear();
	
	if(getRandPercent() < 60)
	{
		checkLoop = 1;
		cashLoop = 0;
		state = "CheckAskTixClerks";
	}
	else
	{
		cashLoop = 1;
		checkLoop = 0;
		state = "CashAskTixClerks";
	}
}

void Manager::BackToCheck()
{
	if(checkQueue->getSize() > numTixTakers){
		state = "CheckAskFoodClerks";
		msgsToSend = numFoodClerks;
		repliesWaiting = numFoodClerks;
	}else{
		state = "CheckAskTixTakers";
		msgsToSend = numTixTakers;
		repliesWaiting = numTixTakers;
	}
}

void Manager::BackToCash()
{
	if(checkQueue->getSize() > numTixTakers){
		state = "CashAskFoodClerks";
		msgsToSend = numFoodClerks;
		repliesWaiting = numFoodClerks;
	}
	else state = "CashClearTixTakers";
}

void Manager::ChATC()
{
	replyTo = checkQueue->Pop(0);
	((TicketClerk*)replyTo)->SendMessage("WhatIsStatus", 1, this);
	msgsToSend = msgsToSend - 1;
	if(msgsToSend == 0){
		state = "CheckWaitTixClerks";
	}
}

void Manager::ChWTC()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("NumInLine");
	if(clerkMsg)
	{
		currentGroup->Append(clerkMsg->Get(0));
		lineLengths->Append(clerkMsg->Get(1));
		repliesWaiting = repliesWaiting - 1;		
		if(repliesWaiting == 0)
		{
			cinema->tixLinesLock->Acquire();
			state = "CheckHandleTixClerks";
		}
	}else{
		msgLock->Release();
		sleepCon->Wait(msgLock);
	}
	msgLock->Release();
}

void Manager::ChHTC()
{
	////Build Outgoing Queue
	//indices of changed clerk
	int changedClerk=-1;
	for(int i = 0; i < numTixClerks; i++)
	{	
		TicketClerk* currTC = (TicketClerk*)(currentGroup->Get(0));
		int currLen = lineLengths->GetInt(1);
		
		//printf("Curr line length %d\n",currLen);
		
		if(currLen == 0 && changedClerk == -1)
		{
			if(getRandPercent() < 20)
			{
				outgoing->Append("GoOnBreak",0);
				changedClerk = i;
			}else{
				outgoing->Append("Okay",0);
			}
		}
		else if(currLen == -1 && changedClerk == -1)
		{
			//printf("BREEEEEEEEEEEEEEEEEAK\n");
			for(int k = 0; k < numTixClerks; k++) //see if anyone is overfull
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 5) //if another line has more than 5 in it
				{
					outgoing->Append("ComeOffBreak",0);
					changedClerk = i;
					break;
				}
			}
			//no one overfull
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}
		else if(currLen < 3 && changedClerk == -1)
		{
			for(int k = 0; k < numTixClerks; k++) //see if anyone else is on duty
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 0) //if another is working and can't be put on break
				{
					if(getRandPercent() < 20)
					{
						outgoing->Append("GoOnBreak",0);
						changedClerk = i;
						break;
					}
				}
			}
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}else
		{
			outgoing->Append("Okay",0);
		}
	}
	
	msgsToSend = numTixClerks;
	state = "CheckSendTixClerks";
}

void Manager::ChSTC()
{
	if(currentGroup->IsEmpty())
	{
		state = "Available";
		cinema->tixLinesLock->Release();
	}else{
		replyTo = currentGroup->Pop(0);
		if(!strcmp(outgoing->GetName(0),"GoOnBreak"))
		{
			printf("Manager has told TicketClerk %s to go on break.\n",((TicketClerk*)replyTo)->myName);
		}
		((TicketClerk*)replyTo)->SendMessage(outgoing->GetName(0), 0);
		outgoing->Pop(0);
	}
	
}

void Manager::ChAFC()
{
	replyTo = checkQueue->Pop(0);
	((FoodClerk*)replyTo)->SendMessage("WhatIsStatus", 1, this);
	msgsToSend = msgsToSend - 1;
	if(msgsToSend == 0){
		state = "CheckWaitFoodClerks";
	}
}

void Manager::ChWFC()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("NumInLine");
	if(clerkMsg)
	{
		currentGroup->Append(clerkMsg->Get(0));
		lineLengths->Append(clerkMsg->Get(1));
		repliesWaiting = repliesWaiting - 1;
		if(repliesWaiting == 0){
			cinema->foodLinesLock->Acquire();
			state = "CheckHandleFoodClerks";
		}
	}else{
		msgLock->Release();
		sleepCon->Wait(msgLock);
	}
	msgLock->Release();
}

void Manager::ChHFC()
{
	////Build Outgoing Queue
	//indices of changed clerk
	int changedClerk=-1;
	
	for(int i = 0; i < numTixClerks; i++)
	{
		FoodClerk* currTC = (FoodClerk*)(currentGroup->Get(0));
		int currLen = lineLengths->GetInt(1);
		
		if(currLen == 0 && changedClerk == -1)
		{
			if(getRandPercent() < 20)
			{
				outgoing->Append("GoOnBreak",0);
				changedClerk = i;
			}else{
				outgoing->Append("Okay",0);
			}
		}
		else if(currLen == -1 && changedClerk == -1)
		{
			for(int k = 0; k < numFoodClerks; k++) //see if anyone is overfull
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 5) //if another line has more than 5 in it
				{
					outgoing->Append("ComeOffBreak",0);
					changedClerk = i;
					break;
				}
			}
			//no one overfull
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}
		else if(currLen < 3 && changedClerk == -1)
		{
			for(int k = 0; k < numFoodClerks; k++) //see if anyone else is on duty
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 0) //if another is working and can't be put on break
				{
					if(getRandPercent() < 20)
					{
						outgoing->Append("GoOnBreak",0);
						changedClerk = i;
						break;
					}
				}
			}
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}else
		{
			outgoing->Append("Okay",0);
		}
	}
	
	msgsToSend = numFoodClerks;
	state = "CheckSendFoodClerks";
}

void Manager::ChSFC()
{
	if(currentGroup->IsEmpty())
	{
		state = "Available";
		cinema->foodLinesLock->Release();
	}else{
		replyTo = currentGroup->Pop(0);
		if(!strcmp(outgoing->GetName(0),"GoOnBreak"))
		{
			printf("Manager has told FoodClerk %s to go on break.\n",((FoodClerk*)replyTo)->myName);
		}
		((FoodClerk*)replyTo)->SendMessage(outgoing->GetName(0), 0);
		outgoing->Pop(0);
	}
}

void Manager::ChATT()
{
	replyTo = checkQueue->Pop(0);
	((TicketTaker*)replyTo)->SendMessage("WhatIsStatus", 1, this);
	msgsToSend = msgsToSend - 1;
	if(msgsToSend == 0){
		state = "CheckWaitTixTakers";
	}
}

void Manager::ChWTT()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("NumInLine");
	if(clerkMsg)
	{
		currentGroup->Append(clerkMsg->Get(0));
		lineLengths->Append(clerkMsg->Get(1));
		repliesWaiting = repliesWaiting - 1;
		if(repliesWaiting == 0){
			cinema->entryLinesLock->Acquire();
			state = "CheckHandleTixTakers";
		}
	}else{
		msgLock->Release();
		sleepCon->Wait(msgLock);
	}
	msgLock->Release();
}

void Manager::ChHTT()
{
	////Build Outgoing Queue
	//indices of changed clerk
	int changedClerk=-1;
	
	for(int i = 0; i < numTixTakers; i++)
	{
		TicketTaker* currTC = (TicketTaker*)(currentGroup->Get(0));
		int currLen = lineLengths->GetInt(1);
		
		if(currLen == 0 && changedClerk == -1)
		{
			if(getRandPercent() < 20)
			{
				outgoing->Append("GoOnBreak",0);
				changedClerk = i;
			}else{
				outgoing->Append("Okay",0);
			}
		}
		else if(currLen == -1 && changedClerk == -1)
		{
			for(int k = 0; k < numTixTakers; k++) //see if anyone is overfull
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 5) //if another line has more than 5 in it
				{
					outgoing->Append("ComeOffBreak",0);
					changedClerk = i;
					break;
				}
			}
			//no one overfull
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}
		else if(currLen < 3 && changedClerk == -1)
		{
			for(int k = 0; k < numTixTakers; k++) //see if anyone else is on duty
			{
				if(i==k) continue;
				if(lineLengths->GetInt(k) > 0) //if another is working and can't be put on break
				{
					if(getRandPercent() < 20)
					{
						outgoing->Append("GoOnBreak",0);
						changedClerk = i;
						break;
					}
				}
			}
			if(changedClerk == -1) outgoing->Append("Okay",0);
		}else
		{
			outgoing->Append("Okay",0);
		}
	}
	
	
	msgsToSend = numTixTakers;
	state = "CheckSendTixTakers";
}

void Manager::ChSTT()
{
	if(currentGroup->IsEmpty())
	{
		state = "Idle";
		cinema->entryLinesLock->Release();
		checkLoop = 0;
	}else{
		replyTo = currentGroup->Pop(0);
		if(!strcmp(outgoing->GetName(0),"GoOnBreak"))
		{
			printf("Manager has told TicketTaker %s to go on break.\n",((TicketTaker*)replyTo)->myName);
		}
		((TicketTaker*)replyTo)->SendMessage(outgoing->GetName(0), 0);
		outgoing->Pop(0);
	}
}

void Manager::CaATC()
{
	replyTo = checkQueue->Pop(0);
	((TicketClerk*)replyTo)->SendMessage("GiveMeMoney", 1, this);
	msgsToSend = msgsToSend - 1;
	if(msgsToSend == 0)
	{
		state = "CashWaitTixClerks";
	}
}

void Manager::CaWTC()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("HereIsMoney");
	if(clerkMsg)
	{
		tixCash = tixCash + clerkMsg->GetFloat(1);
		printf("Manager collected %.2f from %s.\n",clerkMsg->GetFloat(1),((TicketClerk*)clerkMsg->Get(0))->myName);
		repliesWaiting = repliesWaiting - 1;
		//printf("replies awaiting %i\n",repliesWaiting);
		if(repliesWaiting == 0)
		{
			state = "Available";
			//printf("state %s\n",state);
		}
	}else{
		msgLock->Release();
		sleepCon->Wait(msgLock);
	}
	msgLock->Release();
}

void Manager::CaAFC()
{
	replyTo = checkQueue->Pop(0);
	((FoodClerk*)replyTo)->SendMessage("GiveMeMoney", 1, this);
	msgsToSend = msgsToSend - 1;
	if(msgsToSend == 0) state = "CashWaitFoodClerks";
}

void Manager::CaWFC()
{
	msgLock->Acquire();
	IterList* clerkMsg = msgList->Find("HereIsMoney");
	if(clerkMsg)
	{
		foodCash = foodCash + clerkMsg->GetFloat(1);
		printf("Manager collected %.2f from %s.\n",clerkMsg->GetFloat(1),((TicketClerk*)clerkMsg->Get(0))->myName);
		repliesWaiting = repliesWaiting - 1;
		if(repliesWaiting == 0)
		{
			state = "Available";
		}
	}else{
		msgLock->Release();
		sleepCon->Wait(msgLock);
	}
	msgLock->Release();
}

void Manager::CaCTT()
{
	printf("Total money made by office = %f .\n",tixCash+foodCash);
	checkQueue->Clear();
	state = "Idle";
}
